﻿namespace com.stifel.BranchPromotion
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    using com.stifel.BranchMgmtDao;
    using com.stifel.Extensions.EventHandlerExtensions;
    using com.stifel.SharePoint;

    using Microsoft.SharePoint;

    public class BranchPromotion
    {
        #region Fields

        int? _SourceBranchLookupId;
        int? _TargetBranchLookupId;
        StifelBranchPromotionWorklist _WorkList;

        #endregion Fields

        #region Constructors

        public BranchPromotion( Guid currentRequest, SPUserInfoExpanded currentUser, SPWeb currentWeb, string targetSiteUrl )
        {
            CurrentRequest = currentRequest;
            CurrentUser = currentUser;
            CurrentWeb = currentWeb;
            TargetSiteUrl = targetSiteUrl;
        }

        #endregion Constructors

        #region Events

        public event EventHandler<TargetBranchSetEventArgs> TargetBranchSet;

        #endregion Events

        #region Properties

        public Guid CurrentRequest
        {
            get;
            private set;
        }

        public SPUserInfoExpanded CurrentUser
        {
            get;
            private set;
        }

        public SPWeb CurrentWeb
        {
            get;
            private set;
        }

        public int TargetBranchLookupId
        {
            get
            {
                if ( !_TargetBranchLookupId.HasValue )
                {
                    StifelBranchPromotionWorklist worklist = WorkList;
                    if ( worklist != null )
                    {
                        StifelBranchPromotionWorklist.WorkListDetail detail = worklist.Detail;
                        if ( detail != null )
                        {
                            StifelBranchPromotionWorklist.StifelBranchInfo info = detail.BranchInfo;
                            if ( info != null )
                            {
                                _TargetBranchLookupId = info.Id;
                                TargetBranchSet.TryRaise( this, new TargetBranchSetEventArgs( info.Id ) );
                            }
                        }
                    }
                }
                if ( !_TargetBranchLookupId.HasValue )
                    throw new Exception( "Branch promotion failed. Target branch identifier not found." );
                return _TargetBranchLookupId.Value;
            }
            set
            {
                _TargetBranchLookupId = value;
            }
        }

        int SourceBranchLookupId
        {
            get
            {
                if ( !_SourceBranchLookupId.HasValue )
                {
                    StifelBranchInfoDao sourceDao = new StifelBranchInfoDao( CurrentWeb.Url );
                    StifelBranchInfo sourceItem = sourceDao.Get( CurrentRequest );
                    if ( sourceItem.Id.HasValue )
                        _SourceBranchLookupId = sourceItem.Id.Value;
                    else
                        throw new Exception( "Branch promotion failed. Source branch identifier not found." );
                }
                return _SourceBranchLookupId.Value;
            }
            set
            {
                _SourceBranchLookupId = value;
            }
        }

        string TargetSiteUrl
        {
            get;
            set;
        }

        StifelBranchPromotionWorklist WorkList
        {
            get
            {
                if ( _WorkList == null )
                {
                    StifelBranchPromotionWorklistDao dao = new StifelBranchPromotionWorklistDao( CurrentWeb.Url );
                    _WorkList = dao.GetByTitle( CurrentRequest );
                }
                return _WorkList;
            }
            set
            {
                _WorkList = value;
            }
        }

        #endregion Properties

        #region Methods

        public void Copy( BranchPromotionArea area )
        {
            switch ( area )
            {
                case BranchPromotionArea.Hardware:
                    hardware_copy( );
                    break;
                case BranchPromotionArea.Info:
                    branchInfo_copy( );
                    break;
                case BranchPromotionArea.Lease:
                    branchLease_copy( );
                    break;
                case BranchPromotionArea.NetworkEquipment:
                    networkEquipment_copy( );
                    break;
                case BranchPromotionArea.Personnel:
                    branchPersonnel_copy( );
                    break;
                case BranchPromotionArea.Photo:
                    photo_copy( );
                    break;
                case BranchPromotionArea.TelephoneSystem:
                    telephoneSystem_copy( );
                    break;
                case BranchPromotionArea.Update:
                    branchUpdate_copy( );
                    break;
                case BranchPromotionArea.Unit:
                    branchUnit_copy( );
                    break;
                case BranchPromotionArea.Vendor:
                    branchVendor_copy( );
                    break;
                default:
                    break;
            }
        }

        public void Delete( BranchPromotionArea area )
        {
            switch ( area )
            {
                case BranchPromotionArea.Hardware:
                    hardware_delete( );
                    break;
                case BranchPromotionArea.Info:
                    branchInfo_delete( );
                    break;
                case BranchPromotionArea.Lease:
                    branchLease_delete( );
                    break;
                case BranchPromotionArea.NetworkEquipment:
                    networkEquipment_delete( );
                    break;
                case BranchPromotionArea.Personnel:
                    branchPersonnel_delete( );
                    break;
                case BranchPromotionArea.Photo:
                    photo_delete( );
                    break;
                case BranchPromotionArea.TelephoneSystem:
                    telephoneSystem_delete( );
                    break;
                case BranchPromotionArea.Update:
                    branchUpdate_delete( );
                    break;
                case BranchPromotionArea.Unit:
                    branchUnit_delete( );
                    break;
                case BranchPromotionArea.Vendor:
                    branchVendor_delete( );
                    break;
                default:
                    break;
            }
        }

        void branchInfo_copy( )
        {
            StifelBranchInfoDao sourceDao = new StifelBranchInfoDao( CurrentWeb.Url );
            StifelBranchInfo sourceItem = sourceDao.Get( CurrentRequest );

            if ( sourceItem != null )
            {
                StifelBranchPromotionWorklistDao w = new StifelBranchPromotionWorklistDao( CurrentWeb.Url );
                List<StifelBranchPromotionWorklist> l = w.GetByBranchLocation( sourceItem.Id.Value );
                if ( l.Count > 0 )
                {
                    StifelBranchPromotionWorklist.WorkListDetail detail = l[ 0 ].Detail;
                    TargetBranchLookupId = detail.BranchInfo.Id;
                    // RAISE EVENT
                    TargetBranchSet.TryRaise( this, new TargetBranchSetEventArgs( TargetBranchLookupId ) );
                }
                else
                {
                    // add branch info to configured target
                    StifelBranchInfoDao target = new StifelBranchInfoDao( TargetSiteUrl );
                    StifelBranchInfo t = target.Add( sourceItem );
                    if ( t != null )
                    {
                        TargetBranchLookupId = t.Id.Value;
                        // RAISE EVENT
                        TargetBranchSet.TryRaise( this, new TargetBranchSetEventArgs( TargetBranchLookupId ) );

                        // setup restart point for workflow
                        StifelBranchPromotionWorklist wl = new StifelBranchPromotionWorklist
                        {
                            Title = CurrentRequest.ToString( ),
                            //BranchLookupId = sourceItem.Id.Value          //TODO
                        };
                        wl.Detail = new StifelBranchPromotionWorklist.WorkListDetail
                        {
                            BranchInfo = new StifelBranchPromotionWorklist.StifelBranchInfo
                            {
                                BranchLocationIdentifier = t.BranchLocationIdentifier,
                                Id = t.Id.Value,
                                UniqueId = t.UniqueId
                            }
                        };
                        wl = w.Add( wl );
                    }
                }
            }
        }

        void branchInfo_delete( )
        {
            Guid uniqueId = WorkList.UniqueId;
            // delete worklist
            StifelBranchPromotionWorklistDao worklistDao = new StifelBranchPromotionWorklistDao( CurrentWeb.Url );
            worklistDao.Delete( uniqueId );

            Thread.Sleep( 15000 ); // seems to be a timing issue deleting the branch info item perhaps because of the relationship other items has on this item, so wait 15 seconds to allow time for any background processes to complete

            // delete source item
            StifelBranchInfoDao sourceDao = new StifelBranchInfoDao( CurrentWeb.Url );
            sourceDao.Delete( CurrentRequest );
        }

        void branchLease_copy( )
        {
            // get source items
            StifelBranchLeaseDao sourceDao = new StifelBranchLeaseDao( CurrentWeb.Url );
            List<StifelBranchLease> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).BranchLease;

                // get target items
                List<StifelBranchLease> targetItems = getTargetItems<StifelBranchLease>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelBranchLeaseDao targetDao = new StifelBranchLeaseDao( TargetSiteUrl );
                    foreach ( StifelBranchLease o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.BranchLease.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void branchLease_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).BranchLease;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelBranchLeaseDao sourceDao = new StifelBranchLeaseDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void branchPersonnel_copy( )
        {
            // get source items
            StifelBranchPersonnelDao sourceDao = new StifelBranchPersonnelDao( CurrentWeb.Url );
            List<StifelBranchPersonnel> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).BranchPersonnel;

                // get target items
                List<StifelBranchPersonnel> targetItems = getTargetItems<StifelBranchPersonnel>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelBranchPersonnelDao targetDao = new StifelBranchPersonnelDao( TargetSiteUrl );
                    foreach ( StifelBranchPersonnel o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.BranchPersonnel.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void branchPersonnel_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).BranchPersonnel;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelBranchPersonnelDao sourceDao = new StifelBranchPersonnelDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void branchUnit_copy( )
        {
            // get source items
            StifelBranchUnitDao sourceDao = new StifelBranchUnitDao( CurrentWeb.Url );
            List<StifelBranchUnit> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).BranchUnit;

                // get target items
                List<StifelBranchUnit> targetItems = getTargetItems<StifelBranchUnit>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelBranchUnitDao targetDao = new StifelBranchUnitDao( TargetSiteUrl );
                    foreach ( StifelBranchUnit o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.BranchUnit.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void branchUnit_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).BranchUnit;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelBranchUnitDao sourceDao = new StifelBranchUnitDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void branchUpdate_copy( )
        {
            // get source items
            StifelBranchUpdateDao sourceDao = new StifelBranchUpdateDao( CurrentWeb.Url );
            List<StifelBranchUpdate> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).BranchUpdate;

                // get target items
                List<StifelBranchUpdate> targetItems = getTargetItems<StifelBranchUpdate>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    Guid sourceUniqueId = Guid.Empty;
                    Guid targetUniqueId = Guid.Empty;
                    StifelBranchUpdateDao targetDao = new StifelBranchUpdateDao( TargetSiteUrl );
                    // sort ascending
                    targetItems.Sort( ( a, b ) => a.SortKey.CompareTo( b.SortKey ) );
                    foreach ( StifelBranchUpdate o in targetItems )
                    {
                        if ( o.UniqueId != sourceUniqueId )
                        {
                            sourceUniqueId = o.UniqueId;
                            StifelBranchUpdate u = targetDao.Add( o );
                            targetUniqueId = u.UniqueId;
                            StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                            details.BranchUpdate.Add( o.UniqueId );
                            WorkList.SetDetail( details );
                            updateWorkList( );
                        }
                        else
                            targetDao.AddVersion( o, targetUniqueId );
                    }
                }
            }
        }

        void branchUpdate_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).BranchUpdate;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelBranchUpdateDao sourceDao = new StifelBranchUpdateDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void branchVendor_copy( )
        {
            StifelBranchVendorDao sourceDao = new StifelBranchVendorDao( CurrentWeb.Url );
            List<StifelBranchVendor> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).BranchVendor;

                // get target items
                List<StifelBranchVendor> targetItems = getTargetItems<StifelBranchVendor>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelBranchVendorDao targetDao = new StifelBranchVendorDao( TargetSiteUrl );
                    foreach ( StifelBranchVendor o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.BranchVendor.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void branchVendor_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).BranchVendor;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelBranchVendorDao sourceDao = new StifelBranchVendorDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        /// <summary>
        /// Returns a list of type T where the items are not referenced in the work list. The work list contains unique identifiers of those items already written to the target SharePoint list. It is populated with the source unique identifiers when the item is written to the target SharePoint list. The return list of type T is repointed to the target branch identifier.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="worklist"></param>
        /// <returns></returns>
        List<T> getTargetItems<T>( List<T> source, List<Guid> worklist )
            where T : IBranchRelated
        {
            if ( source.Count > 0 )
            {
                List<T> t = new List<T>( );
                if ( worklist != null && worklist.Count > 0 )
                {
                    source.ForEach( a =>
                    {
                        if ( !worklist.Contains( a.UniqueId ) )
                        {
                            // repoint branch location
                            a.BranchLookupId = TargetBranchLookupId;
                            t.Add( a );
                        }
                    } );
                }
                else
                {
                    source.ForEach( a =>
                    {
                        // repoint branch location
                        a.BranchLookupId = TargetBranchLookupId;
                        t.Add( a );
                    } );
                }

                return t;
            }
            else
                return new List<T>( );
        }

        void hardware_copy( )
        {
            // get source items
            StifelHardwareDao sourceDao = new StifelHardwareDao( CurrentWeb.Url );
            List<StifelHardware> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).Hardware;

                // get target items
                List<StifelHardware> targetItems = getTargetItems<StifelHardware>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelHardwareDao targetDao = new StifelHardwareDao( TargetSiteUrl );
                    foreach ( StifelHardware o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.Hardware.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void hardware_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).Hardware;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelHardwareDao sourceDao = new StifelHardwareDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void networkEquipment_copy( )
        {
            // get source items
            StifelNetworkEquipmentDao sourceDao = new StifelNetworkEquipmentDao( CurrentWeb.Url );
            List<StifelNetworkEquipment> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).NetworkEquipment;

                // get target items
                List<StifelNetworkEquipment> targetItems = getTargetItems<StifelNetworkEquipment>( sourceItems, worklist );

                if ( targetItems.Capacity > 0 )
                {
                    // add target items
                    StifelNetworkEquipmentDao targetDao = new StifelNetworkEquipmentDao( TargetSiteUrl );
                    foreach ( StifelNetworkEquipment o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.NetworkEquipment.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void networkEquipment_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).NetworkEquipment;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelNetworkEquipmentDao sourceDao = new StifelNetworkEquipmentDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void photo_copy( )
        {
            // get source items
            StifelPhotoDao sourceDao = new StifelPhotoDao( CurrentWeb.Url );
            List<StifelPhoto> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).Photo;

                // get target items
                List<StifelPhoto> targetItems = getTargetItems<StifelPhoto>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelPhotoDao targetDao = new StifelPhotoDao( TargetSiteUrl );
                    foreach ( StifelPhoto o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.Photo.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void photo_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).Photo;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelPhotoDao sourceDao = new StifelPhotoDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void telephoneSystem_copy( )
        {
            // get source items
            StifelTelephoneSystemDao sourceDao = new StifelTelephoneSystemDao( CurrentWeb.Url );
            List<StifelTelephoneSystem> sourceItems = sourceDao.GetByBranchLocation( SourceBranchLookupId );
            if ( sourceItems.Count > 0 )
            {
                // get worklist
                List<Guid> worklist = WorkList.GetDetail( ).TelephoneSystem;

                // get target items
                List<StifelTelephoneSystem> targetItems = getTargetItems<StifelTelephoneSystem>( sourceItems, worklist );

                if ( targetItems.Count > 0 )
                {
                    // add target items
                    StifelTelephoneSystemDao targetDao = new StifelTelephoneSystemDao( TargetSiteUrl );
                    foreach ( StifelTelephoneSystem o in targetItems )
                    {
                        targetDao.Add( o );
                        StifelBranchPromotionWorklist.WorkListDetail details = WorkList.GetDetail( );
                        details.TelephoneSystem.Add( o.UniqueId );
                        WorkList.SetDetail( details );
                        updateWorkList( );
                    }
                }
            }
        }

        void telephoneSystem_delete( )
        {
            // get worklist
            List<Guid> worklist = WorkList.GetDetail( ).TelephoneSystem;

            if ( worklist.Count > 0 )
            {
                // delete source items
                StifelTelephoneSystemDao sourceDao = new StifelTelephoneSystemDao( CurrentWeb.Url );
                sourceDao.Delete( worklist );
            }
        }

        void updateWorkList( )
        {
            StifelBranchPromotionWorklistDao dao = new StifelBranchPromotionWorklistDao( CurrentWeb.Url );
            dao.Update( WorkList );
        }

        #endregion Methods
    }
}